#include "nanorcs.h"

/* If name refers to a file, call the CheckInFile method in
 * order to check it out, else call CheckInDirectory
 */

void Nanorcs::CheckIn ( char *name, char *version )
{
	if ( IsDirectory ( name ) )
	{
            CheckInDirectory ( name, version );
	}
	else
	{
            CheckInFile ( name, version );
	}
}

void Nanorcs::CheckInFile ( char *filename, char *version )
{
        /* Check if it is a soft link */

        if ( IsSoftLink(filename) )
        {
            CheckInSoftLink(filename);
            return;        
        }

        /* Check if file has more than one hard links */

        struct stat stat;
        lstat(filename, &stat);

        if ( stat.st_nlink > 1 )
        {
            /* If a file with the same inode number was found,
             * return, because checkinhardlink method linked the
             * file. Else check in the file normally.
             */

            if ( CheckInHardLink ( NULL, filename, stat ) == true )
                return;
        }

        /* Ignore back up files */

        if ( filename[strlen(filename) - 1] == '~' )
            return;

        DIR *root = opendir ( "repdir" );

	/* if it's the nanorc's first use, I create the Repository Direcory */

	if ( root == NULL )
	{
		mkdir ( "repdir", 0755 );
	}
	else
	{
		closedir ( root );
	}

	/* Open *.u file */

	char repFilename[MAX_NAME];
    	sprintf ( repFilename, "repdir/%s.u", filename );

	fstream repositoryStream;
	repositoryStream.open ( repFilename );

	/* If repository file doesn't exists */

	if ( repositoryStream == NULL )
	{
		/* Creation of *.u file */
	
		fstream newRepositoryStream ( repFilename, ios::out );
	
		/* Create file's metadata */
	
		Metadata metadata ( filename, version );

                /* At file's begginig there is a number, indicating the
                 * available versions.
                 */

                newRepositoryStream << 1 << endl;

                /* write file's mode */

//                struct stat stat;
//                lstat(filename, &stat);
//                newRepositoryStream << stat.st_mode << endl;

		/* Write metadata */	

                newRepositoryStream << metadata;

                /* Copy file to repository */
	
                fstream file;
                char *buffer;

                file.open ( filename, ios::in );
                file.seekg ( 0, ios::end );
                int size = file.tellg();
                file.clear();
                file.seekg ( 0, ios::beg );

                buffer = new char[size];
                file.read ( buffer, size );
                newRepositoryStream.write ( buffer, size );

                delete buffer;
                file.close();
                newRepositoryStream.close();
        }
	else
	{
            /* If the .u file exists, I create the latest version */

            int versions;

            repositoryStream >> versions;

//            struct stat stat;
//            repositoryStream >> stat.st_mode;

            Metadata metadata;
            repositoryStream >> metadata;

            char *buffer;
            buffer = new char[ metadata.DeltaSize() ];

            repositoryStream.read ( buffer, metadata.DeltaSize() );
            ClearBuffer ( buffer, metadata.DeltaSize() );           //OK

            /* The temporary file, where we built the final version */

            ofstream reconstructedFile;
            reconstructedFile.open ( "reconstructed.tmp", ios::out );

            /* Copy the file's first version to the temp file */

            reconstructedFile.write(  buffer, metadata.DeltaSize() );
            
            reconstructedFile.flush();
            delete buffer;

            /* Build the final file's version */

            for ( int i = 1; i < versions; i++ )                                
            {
                /* Between patches to create the final version */

                fstream delta;
                char *buffer;

                repositoryStream >> metadata;
                buffer = new char[metadata.DeltaSize()];
                
                delta.open ( "delta", ios::out );
                repositoryStream.read ( buffer, metadata.DeltaSize() );
                ClearBuffer ( buffer, metadata.DeltaSize() );
                delta.write( buffer, metadata.DeltaSize() );
                delta.close();
                reconstructedFile.close();

                if ( Patch() == false )
                {
                    cerr << "Error in patch\n";
                    return;
                }
                
                remove("delta");
            }

	    reconstructedFile.close();
            repositoryStream.close();

            /* Find the differences between the last version and the newone's */

            if ( Diff ( filename ) == false )
            {
                cerr << "Error in diff\n";
                return;
            }

            /* Now, in file delta we have the file's latest version's
             * and the newone's differences. The final file's version
             * is in reconstructed.tmp file, which I will delete later.
             */

            /* Create new metadata */

            Metadata newMetadata;                                               
            newMetadata.PreviousMetaData ( metadata );

            /* Write new metadata and new diff result */

            repositoryStream.open ( repFilename );
            repositoryStream.seekp ( 0, ios::end );

            repositoryStream << newMetadata;
            
            /* Copy delta file at the end of reposory file */

            fstream delta;

            delta.open ( "delta", ios::in );
            delta.seekg ( 0, ios::end );
            int size = delta.tellg();
            delta.clear();
            delta.seekg ( 0, ios::beg );

            buffer = new char[size];
            delta.read ( buffer, size );

            repositoryStream.write( buffer, newMetadata.DeltaSize() );

            delete buffer;
            delta.close();

            /* Update the version counter at the start of repository */

            int counter;

            repositoryStream.clear();
            repositoryStream.seekg ( 0, ios::beg );
            repositoryStream >> counter;
            counter++;
            repositoryStream.seekg ( 0, ios::beg );
            repositoryStream << counter;

//            lstat(filename, &stat);
//            repositoryStream << stat.st_mode;

            repositoryStream.close();

            remove("reconstructed.tmp");
            remove("delta");
	}
}

void Nanorcs::CheckInDirectory ( char *directoryName, char *version )
{
	DIR *root = opendir ( "repdir" );

	/* if it's nanorc's first use, I create the Repository Direcory */

	if ( root == NULL )
	{
		mkdir ( "repdir", 0755 );
	}
	else
	{
		closedir ( root );
	}

	/*Create the repository directory */

	char newDirectory[MAX_NAME];
	sprintf ( newDirectory, "./repdir/%s", directoryName );

	/* CheckIn all subdirectories and files in directory */

	struct dirent *dirent;
	DIR *dir = opendir ( directoryName );
	if ( dir == NULL )
		return;

	while ( ( dirent = readdir ( dir ) ) != NULL )
	{
                if ( strcmp ( dirent->d_name, ".." ) == 0 )
                    continue;
                else if (strcmp(dirent->d_name, ".") == 0 )
                {
                    struct stat stat;
                    lstat(".", &stat);
                    mkdir ( newDirectory, stat.st_mode);
                    continue;
                }

		char subdirectory[MAX_NAME];

                sprintf ( subdirectory, "%s/%s", directoryName, dirent->d_name );

		if ( IsDirectory ( subdirectory ) )
		{
                    CheckInDirectory ( subdirectory, version );
		}
		else
		{
			CheckInFile ( subdirectory, version );
		}
	}

	closedir ( dir );
}

/* Copies a soft link in the repository */

void Nanorcs::CheckInSoftLink(char* softLink)
{
    char repositoryName[MAX_NAME];
    sprintf( repositoryName, "repdir/%s", softLink );

    char buffer[MAX_NAME];
    int size = readlink ( softLink, buffer, MAX_NAME );

    fstream repositoryStream;
    repositoryStream.open(repositoryName, ios::out);
    repositoryStream.write(buffer, size);

    repositoryStream.close();
}

bool Nanorcs::CheckInHardLink(const char *path, char* filename, struct stat stat)
{
    /* Look if there is other file with the same inode number*/

    struct dirent *dirent;
    DIR *dir = opendir ( "." );
    bool hardLinkFound = false;

    while ( ( dirent = readdir ( dir ) ) != NULL )
    {
        if ( strcmp ( dirent->d_name, "." ) == 0 || strcmp ( dirent->d_name, ".." ) == 0 )
            continue;
        else
        {
            char subdirectory[MAX_NAME];
            sprintf ( subdirectory, "%s/%s", path, dirent->d_name );

            if ( IsDirectory ( subdirectory ) )
            {
                if ( CheckInHardLink(subdirectory, filename, stat) == true )
                    hardLinkFound = true;
            }
            else
            {
                /* Check if the file has the same inode number */

                struct stat checkInodeStat;
                lstat(dirent->d_name, &checkInodeStat);

                if ( checkInodeStat.st_ino == stat.st_ino )
                {
                    /* if yes, make a hard link to this file */

                    char repFile[MAX_NAME], targetpath[MAX_NAME];

                    if (path == NULL)
                        sprintf(targetpath, "./repdir/%s.u", dirent->d_name);
                    else
                        sprintf(targetpath, "./repdir/%s/%s", path, dirent->d_name);

                    sprintf(repFile, "./repdir/%s.u", filename);
                    link(targetpath, repFile);
                    hardLinkFound = true;
                }
            }
	}
    }
    closedir ( dir );
    return hardLinkFound;
}

/* If name refers to a file, call the CheckOutFile method in
 * order to check it out, else call CheckOutDirectory
 */

void Nanorcs::CheckOut ( char *name, Mode mode, char *variable )
{
        Type type;

        type = ExistsInRepository ( name );

    	if ( type == directory)
	{
            char directory[MAX_NAME];
            sprintf ( directory, "./repdir/%s", name );

            CheckOutDirectory ( directory, mode, variable );

            /* Check out hard links. If there are any */

            CheckOutHardLinks(directory);
	}
        else if ( type == file )
    	{
            char filename[MAX_NAME];
            sprintf ( filename, "./repdir/%s.u", name );

            CheckOutFile ( filename, mode, variable );
	}
}

void Nanorcs::CheckOutDirectory(char* directory, Mode mode, char* variable)
{
	struct dirent *dirent;
	DIR *dir = opendir ( directory );

        if ( dir == NULL )
		return;

        char originalName[MAX_NAME];
        strcpy ( originalName, directory );

        GetOriginalName (originalName);

        /* CheckOut all subdirectories and files in directory */

	while ( ( dirent = readdir ( dir ) ) != NULL )
	{
                if ( strcmp ( dirent->d_name, ".." ) == 0 )
                    continue;
                else if ( strcmp (dirent->d_name, ".") == 0 )
                {
                    /* Create the directory with the right mode */

                    struct stat stat;
                    lstat(dirent->d_name, &stat);
                    mkdir ( originalName, stat.st_mode );
                    continue;
                }

		char name[MAX_NAME];

                sprintf ( name, "%s/%s", directory, dirent->d_name );

		if ( IsDirectory ( name ) == true )
		{
                    CheckOutDirectory ( name, mode, variable );
		}
		else
		{
                    CheckOutFile ( name, mode, variable );
                }
	}

	closedir ( dir );
}

void Nanorcs::CheckOutFile ( char *name, Mode mode, char *variable )
{
    /* Check if is soft link */

    if ( name[strlen(name) - 1] != 'u' )
    {
        CheckOutSoftLink(name);
        return;
    }

    /* Check if file has more than one hard links */

    struct stat stat;
    lstat(name, &stat);

    if ( stat.st_nlink > 1 )
    {
        /* If the .u file has more than one hard links
         * ignore it, in order to create the link
         * in the end
         */

        return;
    }


    fstream repositoryStream;

    repositoryStream.open( name, ios::in );

    int versions;
    repositoryStream >> versions;

//    struct stat stat;
//    repositoryStream >> stat.st_mode;

    Metadata metadata;
    repositoryStream >> metadata;


    /* Creation of the temporary file, where we built the final version */

    fstream reconstructedFile;
    reconstructedFile.open ( "reconstructed.tmp", ios::out );


    char *buffer;
    buffer = new char[ metadata.DeltaSize() ];

    repositoryStream.read ( buffer, metadata.DeltaSize() );
    ClearBuffer ( buffer, metadata.DeltaSize() );

    /* Copy the file's first version to the temp file */

    reconstructedFile.write(  buffer, metadata.DeltaSize() );
    reconstructedFile.flush();

    /* Build file's final version */

    int counter = 1;

    while ( true )
    {

        /* Make compare according to the mode given */

        if ( mode == version )
        {
            char version[MAXVERSION];
            metadata.Version(version);
            
            if ( strcmp ( variable, version ) == 0 )
            {
                break;
            }
        }
        else if ( mode == date )
        {
            char date[12];
            metadata.Date(date);
            if ( strcmp ( variable, date ) == 0 )
            {
                break;
            }
        }
        else if ( mode == tag )
        {
            if ( metadata.GetTag() == stable )
            {
                break;
            }
        }

        /* Check if the repository file ended*/

        if ( counter == versions )
        {
            cout << "Nanorcs failed to find the file: " << name << " with the specifications given\n";

            reconstructedFile.flush();
            reconstructedFile.close();

            return;
        }


        /* Patch in order to create the next version */

        fstream delta;

        repositoryStream >> metadata;
        delete buffer;
        buffer = new char[metadata.DeltaSize()];

        delta.open ( "delta", ios::out );

        repositoryStream.read ( buffer, metadata.DeltaSize() );
        ClearBuffer ( buffer, metadata.DeltaSize() );
        
        delta.write( buffer, metadata.DeltaSize() );
        delta.close();

        if ( Patch() == false )
        {
            cerr << "Error in patch\n";
            return;
        }

        counter++;
    }

    reconstructedFile.close();

    /* The file was found, rename the reconstructed */

    GetOriginalName ( name );


    /* Copy the file to it's original directory */
    
    size_t size;
    reconstructedFile.open ( "reconstructed.tmp" );
    reconstructedFile.seekg ( 0, ios::end );
    size = reconstructedFile.tellg();
    reconstructedFile.seekg ( 0, ios::beg );

    buffer = new char [size];
    reconstructedFile.read ( buffer, size );

    /*cut the .u ending */
    
    name[strlen ( name ) - 2] = '\0';
    fstream checkOutFile ( name, ios::out );
    checkOutFile.write ( buffer, size );
    checkOutFile.close();
    reconstructedFile.close();

//    chmod(name, stat.st_mode);
    delete buffer;
}

/* Checks out a softlink */

void Nanorcs::CheckOutSoftLink(char* name)
{
    char buffer[MAX_NAME];
    fstream repLink;
    repLink.open(name, ios::in);
    repLink.getline(buffer, MAX_NAME);

    name += 9;      //skip "repdir"
    symlink(buffer, name);
}

void Nanorcs::CheckOutHardLinks(char *path, char *targetname, __ino_t inode )
{
    struct dirent *dirent, *dirent2;
    DIR *dir = opendir(path);

    while ( ( dirent = readdir ( dir ) ) != NULL )
    {
        if ( strcmp ( dirent->d_name, "." ) == 0 || strcmp ( dirent->d_name, ".." ) == 0 )
            continue;
        else
        {
            char subdirectory[MAX_NAME];
            sprintf ( subdirectory, "%s/%s", path, dirent->d_name );

            if ( IsDirectory ( subdirectory ) )
            {
                CheckOutHardLinks(subdirectory);
            }
            else
            {
                /* Check if the file has the same inode number */

                struct stat checkInodeStat;
                lstat(dirent->d_name, &checkInodeStat);

                if ( checkInodeStat.st_nlink == inode )
                {
                    /* If this file is a hard link to a requested
                     * from previous call of CheckOutHardLinks,
                     * hard link it to the target file
                     */

                    targetname += 9;                //cut repdir
                    targetname[strlen(targetname) - 2] = '\0';  //cut the ending .u

                    char *hardLink = new char[MAX_NAME];
                    path += 9;
                    sprintf(hardLink, "%s/%s", path, dirent->d_name);
                    hardLink[strlen(hardLink - 2)] = '\0';
                    hardLink += 9;

                    link(targetname, hardLink);
                }

                if ( checkInodeStat.st_nlink > 1 && dirent->d_name[strlen(dirent->d_name) - 1] == 'u')
                {
                    /* If there are hard links pointing this file
                     *and it is not a soft link, search for this inode
                     * to the subdirectories.
                     */

                    char targetfile[MAX_NAME];
                    sprintf(targetfile, "%s/%s", path, dirent->d_name);

                    DIR *directory = opendir("./");

                    while ( ( dirent2 = readdir ( directory ) ) != NULL )
                    {

                        if ( strcmp ( dirent2->d_name, "." ) == 0 || strcmp ( dirent2->d_name, ".." ) == 0 )
                            continue;

                        char subdirectory[MAX_NAME];
                        sprintf ( subdirectory, "%s/%s", path, dirent->d_name );

                        if ( IsDirectory ( subdirectory ) )
                        {
                            CheckOutHardLinks(subdirectory, targetfile, checkInodeStat.st_ino);
                        }
                    }
                }
            }
        }
    }
    closedir ( dir );
}

void Nanorcs::TagProject ( char *name )
{
    /* If name refers to a file, call the CheckOutPatching method in
     * order to check it out.
     */

     if ( IsDirectory ( name ) == true )
     {
         char directory[MAX_NAME];
         sprintf ( directory, "./repdir/%s", name );

         TagDirectory ( directory );
     }
     else
     {
         char filename[MAX_NAME];
         sprintf ( filename, "./repdir/%s.u", name );

         TagFile ( filename );
     }
}

void Nanorcs::TagDirectory ( char* name )
{

	struct dirent *dirent;
	DIR *dir = opendir ( name );
	if ( dir == NULL )
		return;

        /* CheckOut all subdirectories and files in directory */

	while ( ( dirent = readdir ( dir ) ) != NULL )
	{
                if ( strcmp ( dirent->d_name, "." ) == 0
                  || strcmp ( dirent->d_name, ".." ) == 0 )
                    continue;

		char subdirectory[MAX_NAME];

                sprintf ( subdirectory, "%s/%s", name, dirent->d_name );

		if ( IsDirectory ( subdirectory ) == true )
		{
                    TagDirectory ( subdirectory );
                }
		else
		{
                    TagFile ( subdirectory );
                }
	}

	closedir ( dir );
}

void Nanorcs::TagFile ( char* filename )
{
    char repFilename[MAX_NAME];
    sprintf ( repFilename, "%s", filename );

    fstream repositoryStream;
    repositoryStream.open ( repFilename, ios::in | ios::out);

    int versions;
    repositoryStream >> versions;

    /* Read Repository file till end */

    char *buffer;
    Metadata metadata;
    size_t pointer;

    for ( int i = 0; i < versions; i++ )
    {
        pointer = repositoryStream.tellg();
        repositoryStream >> metadata;
        buffer = new char[metadata.DeltaSize()];
        repositoryStream.read ( buffer, metadata.DeltaSize() );
        delete buffer;
    }

    repositoryStream.seekp ( pointer, ios::beg );

    metadata.Stable();
    repositoryStream << endl << metadata;
    repositoryStream.close();
}

void Nanorcs::Changes(char* name, char *input )
{

        Type type;

        type = ExistsInRepository ( name );

    	if ( type == directory)
	{
            char directory[MAX_NAME];
            sprintf ( directory, "./repdir/%s", name );

            if ( input != NULL )
                ChangesDirectory ( directory, input );
            else
            {
                cerr << "Changes with version range for directories is not permitted\n";
                return;
            }
	}
        else if ( type == file )
    	{
            char filename[MAX_NAME];
            sprintf ( filename, "./repdir/%s.u", name );

            ChangesFile ( filename, input );
	}
}

void Nanorcs::ChangesDirectory ( char* directory, char *input )
{
        struct dirent *dirent;
	DIR *dir = opendir ( directory );

        if ( dir == NULL )
		return;

        /* Create the directory */

        char originalName[MAX_NAME];
        strcpy ( originalName, directory );

        GetOriginalName (originalName);
        mkdir ( originalName, 0755 );

        /* CheckOut all subdirectories and files in directory */

	while ( ( dirent = readdir ( dir ) ) != NULL )
	{
                if ( strcmp ( dirent->d_name, "." ) == 0
                  || strcmp ( dirent->d_name, ".." ) == 0 )
                    continue;

		char name[MAX_NAME];

                sprintf ( name, "%s/%s", directory, dirent->d_name );

		if ( IsDirectory ( name ) == true )
		{
                    ChangesDirectory ( name, input );
		}
		else
		{
                    ChangesFile ( name, input );
                }
	}

	closedir ( dir );

}

void Nanorcs::ChangesFile(char* filename, char *input )
{
    fstream repositoryStream;
    repositoryStream.open( filename, ios::in );

    int versions;
    repositoryStream >> versions;

    Metadata metadata;
    repositoryStream >> metadata;


    /* Creation of the temporary file, where we built the final version */

    fstream reconstructedFile;
    reconstructedFile.open ( "reconstructed.tmp", ios::out );


    char *buffer;
    buffer = new char[ metadata.DeltaSize() ];

    repositoryStream.read ( buffer, metadata.DeltaSize() );
    ClearBuffer ( buffer, metadata.DeltaSize() );

    /* Copy the file's first version to the temp file */

    reconstructedFile.write(  buffer, metadata.DeltaSize() );
    reconstructedFile.close();

    ChangesMode mode = GetChangesMode ( input );
    char *firstBound, *secondBound;
    firstBound = new char[MAXVERSION];      //komparsiliki
    secondBound = new char[MAXVERSION];

    if ( mode == versionRange || mode == dateRange )
    {
        GetBounds ( input, &firstBound, &secondBound );
    }
    else if ( mode == fixedVersion || mode == fixedDate )
    {
        input += 2;     //skip -r/d
        strcpy ( firstBound, input );
    }

    //DEBUG
//    strcpy (firstBound, "1.1");
 //   strcpy (secondBound, "1.2");

    if ( mode == last || mode == fixedVersion || mode == fixedDate )
    {
        /* Keep the first version */

        fstream firstVersion;
        firstVersion.open("first", ios::out);
        firstVersion.write(buffer, metadata.DeltaSize());
        firstVersion.close();
    }

    delete buffer;

    /* Build file's target version */

    bool firstBoundVersionFound = false;
    bool secondBoundVersionFound = false;

    for ( int i = 1; i < versions; i++ )
    {
        fstream delta;

        repositoryStream >> metadata;
        buffer = new char[metadata.DeltaSize()];

        delta.open ( "delta", ios::out );

        repositoryStream.read ( buffer, metadata.DeltaSize() );
        ClearBuffer ( buffer, metadata.DeltaSize() );

        delta.write( buffer, metadata.DeltaSize() );
        delta.close();
        delete buffer;

        /* Patch in order to create the next version */

        if ( Patch() == false )
        {
            cerr << "Error in patch\n";
            return;
        }

        if ( mode == dateRange || mode == versionRange )
        {
            char date[12], currentVersion[MAXVERSION];
            metadata.Date(date);
            metadata.Version(currentVersion);

            if ( mode == dateRange  )
            {
                if ( strcmp (firstBound, date) == 0 )
                {
                    /* If we have range query and found the first bound, keep it */

                    fstream firstVersion;
                    fstream reconstructed;

                    reconstructed.open("reconstructed.tmp", ios::in );

                    reconstructed.seekg(0, ios::end);
                    int size = reconstructed.tellg();
                    reconstructed.seekg(0, ios::beg);
                    buffer = new char[size];
                    reconstructed.read(buffer, size);

                    firstVersion.open("first", ios::out);
                    firstVersion.write(buffer, size);
                    firstVersion.close();
                    reconstructed.close();
                    firstBoundVersionFound = true;
                }
                else if ( strcmp (secondBound, date) == 0 )
                {
                    /* If we have range query and found the second bound, break
                     *the reconstruction loop
                     */

                    secondBoundVersionFound = true;
                    break;
                }
            }
            else if ( mode == versionRange )
            {
                if ( strcmp (firstBound, currentVersion) == 0 )
                {
                    /* If we have range query and found the first bound, keep it */

                    fstream firstVersion;
                    fstream reconstructed;

                    reconstructed.open("reconstructed.tmp", ios::in );

                    reconstructed.seekg(0, ios::end);
                    int size = reconstructed.tellg();
                    reconstructed.seekg(0, ios::beg);
                    buffer = new char[size];
                    reconstructed.read(buffer, size);

                    firstVersion.open("first", ios::out);
                    firstVersion.write(buffer, size);
                    firstVersion.close();
                    reconstructed.close();
                    secondBoundVersionFound = true;

                }
                else if ( strcmp (secondBound, currentVersion) == 0 )
                {
                    /* If we have range query and found the second bound, break
                     *the reconstruction loop
                     */

                    secondBoundVersionFound = true;
                    break;
                }
            }
        }
    }

    if ( ( mode == versionRange || mode == dateRange) &&
         (secondBoundVersionFound == false || secondBoundVersionFound == false ) )
    {
        cerr << "One of the two bounds in the query was not found";
        return;
    }

    /* Compare the last version with the first-one*/

    if ( Diff("first")  == false )
    {
        cerr << "Error in diff\n";
        return;
    }

    fstream delta;
    unsigned int size;
    delta.open("delta", ios::in);
    delta.seekg(0, ios::end);
    size = delta.tellg();
    buffer = new char[size];
    delta.seekg(0, ios::beg);

    delta.read(buffer, size);
    delta.close();

    cout << "Changes between first and last version of " << filename <<": \n";
    cout.write(buffer, size);

    delete buffer;
    remove("reconstructed.tmp");
    remove("first");
    remove("delta");
}

ChangesMode Nanorcs::GetChangesMode(char* input)
{
    if ( input == NULL )
        return last;

    char *pointer = input + 1;

    while ( *pointer != '\0' )
    {
        /* if we have range query */

        if (*pointer == '-')
        {
            if (input[1] == 'r')
                return versionRange;
            else if (input[1] == 'd')
                return dateRange;
            else return error;
        }
        else
        {
            pointer++;
        }
    }

    /* If loop terminates, we don't have range query */

    if (input[1] == 'r')
        return fixedVersion;
    else if (input[1] == 'd')
        return fixedDate;
    else
        return error;

}

/* Retuns true if argument is direcotry, else returns false */

bool Nanorcs::IsDirectory ( char *argument )
{
    DIR *directory = opendir ( argument );

    if ( directory == NULL )
    {
        return false;
    }
    else
    {
        closedir ( directory );
        return true;
    }
}

/* This method calls diff and stores the output in delta file */

bool Nanorcs::Diff ( const char *filename )
{
   int fd[2];
   pid_t cpid;
   int ffd, n;
   char buf[10];

   if( pipe(fd) == -1) {
      perror("pipe");
      return false;
   }

   if( (cpid = fork()) == -1 ) {

      perror("fork");
      return false;
   }

   if( cpid == 0 ) {

      close(fd[0]);
      close(0);
      dup2(fd[1], 1); /*Child's stdout is set to write end of pipe*/
      execlp("diff", "diff", "reconstructed.tmp", filename, NULL);

   }
   else {

      close(fd[1]);
      if( (ffd = open("delta", O_WRONLY | O_CREAT, 0666) ) == -1 ) {
         perror("open");
         return false;
      }

      /*Parent reads from read end of pipe*/
      while( (n=read(fd[0], buf, sizeof(buf))) > 0 ) {

         if( write(ffd, buf, n) < 0 ) {
            perror("write");
            return false;
         }

      }
      close(fd[0]);
      wait();
   }
   return true;
}

/* Patches the delta in recostructed.tmp  */

bool Nanorcs::Patch ( void )
{
int fd[2];
   pid_t cpid;
   int ffd, n;
   char buf[32];

   if( pipe(fd) == -1) {
      perror("pipe");
      return false;
   }

   if( (cpid = fork()) == -1 ) {

      perror("fork");
      return false;
   }

   if( cpid == 0 ) {

      close(fd[1]);
      close(1); 
      dup2(fd[0], 0); /*Child's stdin is set to read end of pipe*/
      execlp("patch", "patch", "reconstructed.tmp", NULL);

   }
   else {

      close(fd[0]);
      if( (ffd = open("delta",O_RDONLY)) == -1 ) {
         perror("open");
         return false;
      }

      /*Parent writes to write end of pipe*/
      while( (n=read(ffd, buf, sizeof(buf))) > 0 ) {

         if( write(fd[1], buf, n) < 0 ) {
            perror("write");
            return false;
         }

      }
      close(fd[1]);
      wait();
   }

   return true;
}

/* Cuts tha path before the filename */

void Nanorcs::GetOriginalName ( char *filename )
{
    int slash = 2;

    /* find where the filename starts */

    while ( filename[slash] != '\0' )
    {
        if ( filename[slash] == '/' )
        {
            slash++;
            break;
        }
        else
        {
            slash++;
        }
    }

    for ( int j = 0; filename[j] != '\0'; j++ )
    {
            filename[j] = filename[slash + j];
    }
}

void Nanorcs::ClearBuffer ( char *buffer, int size )
{
    for ( int i = 0; i < size; i++ )
        buffer[i] = buffer[i + 1];
    buffer[size - 1] = '\n';
}

Type Nanorcs::ExistsInRepository ( char *name )
{
	struct dirent *dirent;
	DIR *dir = opendir ( "./repdir" );

        if ( dir == NULL )
		return notFound;

        /* Cut the last '/' if exists */

        if ( name[strlen( name ) - 1] == '/' )
            name[strlen( name ) - 1] = '\0';

	while ( ( dirent = readdir ( dir ) ) != NULL )
	{
                if ( strcmp ( dirent->d_name, "." ) == 0
                        || strcmp ( dirent->d_name, ".." ) == 0 )
                    continue;

                char repositoryName[MAX_NAME];
                sprintf ( repositoryName, "./repdir/%s", name );

                if ( IsDirectory ( repositoryName ) && strcmp ( dirent->d_name, name ) == 0 )
		{
                    closedir ( dir );
                    return directory;
		}
		else
                {
                    char filename[MAX_NAME];
                    sprintf(filename, "%s.u", name);
                    
                    if ( strcmp ( dirent->d_name, filename ) == 0)
                    {
                        closedir ( dir );
                        return file;
                    }
                }
	}

	closedir ( dir );
        return notFound;
}

bool Nanorcs::IsSoftLink ( char *name )
{
    char buffer[MAX_NAME];

    return readlink ( name, buffer, 1024 ) != -1;
}

void Nanorcs::GetBounds ( char *input, char **firstBound, char **secondBound )
{
    bool firstBoundCopied = false;
    char *pointer = &input[2];
    int i = 0;

    while ( *pointer != '\0')
    {
        if ( *pointer == '-' )
        {
            firstBoundCopied = true;
            (*firstBound)[i] = '\0';
            i = 0;
            pointer++;

            continue;
        }

        if ( firstBoundCopied == false )
            (*firstBound)[i] = *pointer;
        else
            (*secondBound)[i] = *pointer;

        i++;
        pointer++;
    }

    (*secondBound)[i] = '\0';
}
